#include "stdafx.h"

#include <TrueText/Stream.h>
#include <TrueText/Face.h>
#include <TrueText/Font.h>
#include <TrueText/Atlas.h>
#include <TrueText/TrueTextLibrary.h>
#include <TrueText/BitmapInfo.h>
#include <TrueText/TextMetrics.h>
#include <TrueText/AtlasText.h>
#include <TrueText/AtlasTextBuilder.h>
#include <TrueText/Text.h>

struct HandleDtor
{
	HandleDtor(HANDLE hFile)
		: hFile(hFile) { }
	~HandleDtor()
	{
		CloseHandle(hFile);
	}
private:
	HANDLE hFile;
};

struct MapViewDtor
{
	MapViewDtor(LPVOID mapViewAddr)
		: mapViewAddr(mapViewAddr) { }
	~MapViewDtor()
	{
		UnmapViewOfFile(mapViewAddr);
	}
private:
	LPVOID mapViewAddr;
};

struct StreamWithHandles
{
	TrueText::ptr<TrueText::Stream>::shared stream;
	std::auto_ptr<HandleDtor> fileDtor;
	std::auto_ptr<HandleDtor> mappingDtor;
	std::auto_ptr<MapViewDtor> mapViewDtor;
};

StreamWithHandles CreateFontStream()
{
	HANDLE hFile = CreateFile(L"lithosb.ttf", 
		GENERIC_READ,
		FILE_SHARE_READ,
		0,
		OPEN_EXISTING,
		0,
		0);

	if (hFile == INVALID_HANDLE_VALUE)
	{
		std::cout << "CreateFile failed";
		return StreamWithHandles();
	}

	std::auto_ptr<HandleDtor> fileDtor(new HandleDtor(hFile));

	HANDLE fileMapping = CreateFileMapping(
		hFile,
		0,
		PAGE_READONLY,
		0,
		0,
		0);

	if (fileMapping == INVALID_HANDLE_VALUE)
	{
		std::cout << "CreateFileMapping failed";
		return StreamWithHandles();
	}

	std::auto_ptr<HandleDtor> mappingDtor(new HandleDtor(fileMapping));

	LPVOID mapView = MapViewOfFile(
		fileMapping, 
		FILE_MAP_READ,
		0,
		0,
		0);

	if (!mapView)
	{
		std::cout << "MapViewOfFile failed";
		return StreamWithHandles();
	}

	std::auto_ptr<MapViewDtor> mapViewDtor(new MapViewDtor(mapView));

	TrueText::ptr<TrueText::Stream>::shared stream(new TrueText::MemoryStream(mapView, GetFileSize(hFile, 0)));

	StreamWithHandles result;
	result.stream = stream;
	result.fileDtor = fileDtor;
	result.mappingDtor = mappingDtor;
	result.mapViewDtor = mapViewDtor;

	return result;
}

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
	UINT  num = 0;          // number of image encoders
	UINT  size = 0;         // size of the image encoder array in bytes

	Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;

	Gdiplus::GetImageEncodersSize(&num, &size);
	if(size == 0)
		return -1;  // Failure

	pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
	if(pImageCodecInfo == NULL)
		return -1;  // Failure

	GetImageEncoders(num, size, pImageCodecInfo);

	for(UINT j = 0; j < num; ++j)
	{
		if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
		{
			*pClsid = pImageCodecInfo[j].Clsid;
			free(pImageCodecInfo);
			return j;  // Success
		}    
	}

	free(pImageCodecInfo);
	return -1;  // Failure
}

int _tmain(int argc, _TCHAR* argv[])
{
	TrueText::TrueTextLibrary lib;
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	StreamWithHandles stream = CreateFontStream();
	if (!stream.stream.get())
	{
		std::cout << "Failed to create font stream.";
		return 1;
	}

	TrueText::Face face("lithosb.ttf", [&stream] () { return stream.stream; });
	TrueText::Font font(face);
	font.SetHeight(32);
	font.SetColor(TrueText::Color(255, 255, 255, 255));

	TrueText::Atlas a(font);
	a.SetString(L"0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz.,:!? ");
	a.SetClipWidth(256);
	TrueText::TextBounds b = a.GetBounds();
	TrueText::ptr<TrueText::AtlasMetrics>::shared m = a.GetMetrics();

	int w = b.GetDimensions().Width;
	int h = b.GetDimensions().Height;

	{
        std::auto_ptr<Gdiplus::Bitmap> bitmap(new Gdiplus::Bitmap(w, h, PixelFormat32bppARGB));
        Gdiplus::Rect lockedRect(0, 0, w, h);
        Gdiplus::BitmapData bitmapData;
        bitmap->LockBits(&lockedRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData);
    
        TrueText::BitmapInfo bi;
        bi.SetBits(bitmapData.Scan0);
        bi.SetBounds(b.GetDimensions());
        bi.SetPitch(bitmapData.Stride);
        bi.SetPixelFormat(TrueText::PIXEL_FORMAT_ARGB_8888);
    
        a.Render(bi);
    
        bitmap->UnlockBits(&bitmapData);

		std::auto_ptr<Gdiplus::Bitmap> atlas(bitmap->Clone(0, 0, w, h, PixelFormat32bppARGB));

		Gdiplus::Pen borderPen(Gdiplus::Color(255, 0, 0, 255));
		Gdiplus::Pen pointPen(Gdiplus::Color(120, 255, 0, 0));
		Gdiplus::Pen rectPen(Gdiplus::Color(50, 0, 255, 0));
		Gdiplus::Graphics g(bitmap.get());
		g.DrawRectangle(&borderPen, 0, 0, w-1, h-1);
		for (std::map<wchar_t, TrueText::LetterMetrics>::const_iterator i = m->Letters.begin(); i != m->Letters.end(); ++i)
		{
            g.DrawEllipse(&pointPen, i->second.origin.x + m->Offset.x, i->second.origin.y + m->Offset.y, 3, 3);
			g.DrawRectangle(&rectPen, 
				i->second.bounds.left + m->Offset.x, 
				i->second.bounds.top + m->Offset.y, 
				i->second.bounds.right - i->second.bounds.left, 
				i->second.bounds.bottom - i->second.bounds.top);
		}

		const wchar_t* str = L"Cowboy gay sex";

		TrueText::AtlasTextBuilder ab;
		ab.SetAtlasMetrics(m);
		ab.SetString(str);
		TrueText::AtlasText at = ab.GetAtlasText();
		TrueText::TextBounds atb = ab.GetBounds();

		std::auto_ptr<Gdiplus::Bitmap> textBitmap(new Gdiplus::Bitmap(atb.GetDimensions().Width, atb.GetDimensions().Height, PixelFormat32bppARGB));
		Gdiplus::Graphics textGraphics(textBitmap.get());

		for (std::vector<TrueText::AtlasTextEntry>::const_iterator i = at.Entries.begin(); i != at.Entries.end(); ++i)
		{
			textGraphics.DrawImage(atlas.get(), i->At.x, i->At.y, i->Area.left, i->Area.top,
				i->Area.right - i->Area.left + 1, i->Area.bottom - i->Area.top + 1, Gdiplus::UnitPixel);
		}

		TrueText::Text oldText(font);
		oldText.SetString(str);
		TrueText::TextBounds oldDims = oldText.GetBounds();

		std::auto_ptr<Gdiplus::Bitmap> ethalonBitmap(new Gdiplus::Bitmap(oldDims.GetDimensions().Width, oldDims.GetDimensions().Height, PixelFormat32bppARGB));
		lockedRect.Width = oldDims.GetDimensions().Width;
		lockedRect.Height = oldDims.GetDimensions().Height;
        ethalonBitmap->LockBits(&lockedRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData);

        bi.SetBits(bitmapData.Scan0);
        bi.SetBounds(oldDims.GetDimensions());
        bi.SetPitch(bitmapData.Stride);
        bi.SetPixelFormat(TrueText::PIXEL_FORMAT_BGRA_8888);

		oldText.Render(bi);

        ethalonBitmap->UnlockBits(&bitmapData);

		CLSID pngClsid;
		GetEncoderClsid(L"image/png", &pngClsid);
		bitmap->Save(L"atlas.png", &pngClsid, 0);
		textBitmap->Save(L"text.png", &pngClsid, 0);
		ethalonBitmap->Save(L"perfect_text.png", &pngClsid, 0);
	}
	Gdiplus::GdiplusShutdown(gdiplusToken);
	return 0;
}

#pragma comment (lib, "TrueText.lib")
#pragma comment (lib, "Gdiplus.lib")